from numpy import *
from datetime import date

class RIKBBondGenre:
    (RIKB101210, RIKB110722, RIKB130517, RIKB190226, RIKB250612) = range(5)

class RIKBBond:
    settlementDay = 0
    ISKBondGenre = 4

    def __init__(self, ISKBondGenre, settlementDay_):
        global Idxs, cf
        self.settlementDay = settlementDay_
        self.ISKBondGenre = ISKBondGenre

        # RIKB101210
        if ISKBondGenre == 0:
            indexed =           0
            issueDate =         date(2008,12,10)
            firstinterestDate = date(2008,12,10)
            maturityDate =      date(2010,12,10)
            self.fixedRate =    13.75
            faceAmount =        100
        # RIKB110722
        elif ISKBondGenre == 1:
            indexed =           0
            issueDate =         date(2009,8,26)
            firstinterestDate = date(2009,7,22)
            maturityDate =      date(2011,7,22)
            self.fixedRate =    8.00
            faceAmount =        100
        # RIKB130517
        elif ISKBondGenre == 2:
            indexed =           0
            issueDate =         date(2002,5,17)
            firstinterestDate = date(2002,5,17)
            maturityDate =      date(2013,5,17)
            self.fixedRate =    7.25
            faceAmount =        100
        # RIKB190226
        elif ISKBondGenre == 3:
            indexed =           0
            issueDate =         date(2008,2,26)
            firstinterestDate = date(2008,2,26)
            maturityDate =      date(2019,2,26)
            self.fixedRate =    8.75
            faceAmount =        100
        # RIKB250612
        elif ISKBondGenre == 4:
            indexed =           0
            issueDate =         date(2009,6,12)
            firstinterestDate = date(2009,6,12)
            maturityDate =      date(2025,6,12)
            self.fixedRate =    8.00
            faceAmount =        100
        else:
            print 'error'
            # ATH: cast exception if none above, implemented when I know how to do it

        n = maturityDate.year - firstinterestDate.year

        # Annual coupon payment days we get from the maturity date
        day_ = maturityDate.day
        month_ = maturityDate.month

        # Bond matrix (columns)
        #   0. Cashflow dates
        #   1. Cashflows amounts
        #   2. Days, from now, until cashflow is made (0 in the past i.e. no negative days)
        #   3. Year-on-Year daycount rule (365 or 366 , actual basis)
        #   4. Yearfrac
        #   5. Discounted remaining cashflows by market yield
        self.cf = zeros((n+1,6))

        # Issue date
        self.cf[0,0] = firstinterestDate.toordinal()

        self.cf[:,1] = self.fixedRate
        self.cf[n,1] = self.fixedRate+faceAmount

        for i in range(1,n+1):
            d = date(firstinterestDate.year+i,month_,day_)
            self.cf[i,0] = d.toordinal()
            self.cf[i,2] = maximum(d.toordinal() - self.settlementDay.toordinal(),0)
            self.cf[i,3] = self.cf[i,0] - self.cf[i-1,0]

        # Days to next coupon payment (>0, otherwise already paid)
        # Note Idxs[0] references the row of next cashflow date (coupon and
        # interest payment)
        self.Idxs = nonzero(self.cf[:,2]>0)
        self.Idxs = self.Idxs[0]

        # Calculating yearfrac according to ACT/ACT ISMA, finding the first
        # yearfrac and use it for each subsequent year
        DaysAccrued = self.cf[self.Idxs[0],2]
        DaysInPeriod = self.cf[self.Idxs[0],3]
        DayFrac = DaysAccrued/DaysInPeriod
        self.cf[self.Idxs,4] = arange(0,size(self.Idxs))+DayFrac

    #def duration(self, ytm, DurationType, settlementDay_=settlementDay):
    #def convexity(self, ytm, settlementDay_=settlementDay):
    #def basisPointValue(self, ytm, settlementDay_=settlementDay):
    #def bondmatrix():

    def cleanPrice(self, ytm, settlementDay_=None):
        ytm = ytm*100

        if settlementDay_ is not None:
            self.__init__(self.ISKBondGenre,settlementDay_)
        
        # Accrued interests
        Accr = (1-self.cf[self.Idxs[0],4])*self.fixedRate

        # Discounted remaining cashflows
        self.cf[self.Idxs,5]=(1/(1+ytm/100))**self.cf[self.Idxs,4]
        PV = sum(self.cf[self.Idxs,1]*self.cf[self.Idxs,5])
        return PV-Accr        

    def dirtyPrice(self, ytm, settlementDay_=None):
        ytm = ytm*100

        if settlementDay_ is not None:
            self.__init__(self.ISKBondGenre,settlementDay_)
        
        # Discounted remaining cashflows
        self.cf[self.Idxs,5]=(1/(1+ytm/100))**self.cf[self.Idxs,4]
        PV = sum(self.cf[self.Idxs,1]*self.cf[self.Idxs,5])
        return PV

    def yieldFromClean(self, price, ACCURACY=0.0001, MAXITERATIONS=1000):
        bot = 0
        top = 1
        tempprice = self.cleanPrice(top)
        yld = 0.5*(top+bot)
        for i in range(MAXITERATIONS):
            tempprice = self.cleanPrice(yld)
            diff = tempprice - price
            if abs(diff)<ACCURACY:
                return yld
            if diff>0:
                bot = yld
            else:
                top = yld
            yld = 0.5*(top+bot)
        return yld

    def fwdPrice(self, price, rate, expiryDay, settlementDay_=None):
        if settlementDay_ is not None:
            self.__init__(self.ISKBondGenre,settlementDay_)

        daysUntilExpiry = expiryDay.toordinal() - self.settlementDay.toordinal()

        # cf2 is only remaining cashflows, 
        t1 = nonzero(self.cf[:,2]>0)
        cf2 = self.cf[t1[0],:]

        # t2 is cashflows until expiry of forward
        t2 = nonzero(cf2[:,2]<daysUntilExpiry)
        t2 = t2[0]

        I = 0
        for i in t2:
            I = I+cf2[i,1]*1/(1+rate*cf2[i,2]/360)
       
        return (1+rate*daysUntilExpiry/360)*(price-I)
